package leo.peersim.common;

import java.util.Vector;

import org.apache.log4j.Logger;

import peersim.core.Network;
import peersim.core.Node;

public class QueueConsistencyValidator {
	
	private Logger logger = Logger.getLogger(this.getClass().getName());
	
	private int _protocolID;
	
	private int _FSMQueueSize = 0; // it makes sense iff consistency is true
	private int _numOfLiveNodes = 0;
	private int _numOfFailedNodes = 0;
	private Vector<String> _atomicQueue = null;
	
	private boolean _consistency;
	
	public QueueConsistencyValidator(int protocolID) {
		this._protocolID = protocolID;
		this._consistency = this.checkConsistency();
	}
	
	public int getQueueSize() {
		if (this._consistency) {
			return this._FSMQueueSize;
		}
		return -1;
	}
	
	public boolean isConsistent() {
		return this._consistency;
	}

	public int getNumOfFailedNodes() {
		return this._numOfFailedNodes;
	}

	public int getNumOfLiveNodes() {
		return this._numOfLiveNodes;
	}
	
	public Vector<String> getAtomicQueue() {
		return this._atomicQueue;
	}
	
	private boolean checkConsistency() {
		
		if (!this.doesEveryoneHaveTheSameSize()) {
			return false;
		}
		
		int DEBUG_numOfComparisons = 0;
		Vector<String> cqueue = new Vector<String>();
		boolean filled = false;
		
		for (int i=0; i<Network.size(); i++) {
			Node n = Network.get(i);
			IFSAutomaton iau = (IFSAutomaton) n.getProtocol(_protocolID);
			
			if (!n.isUp()) {
				continue;
			}
			
			_atomicQueue = iau.getCommandHistory();
			for (int k=0; k<_atomicQueue.size(); k++) {
				
				if (!filled) {
					String compValue = _atomicQueue.get(k).toString();
					cqueue.add(compValue);
				} else {
					DEBUG_numOfComparisons++;
					int res = cqueue.get(k).compareTo(_atomicQueue.get(k).toString());
					if (res!=0) {
						return false;
					}
				}	
			}
			filled = true;		
		}
		//logger.debug(dq + " atomically broadcast on " + _numOfLiveNodes + " agents (" + _numOfFailedNodes + " failed)");
		return true;
	}
	
	private boolean doesEveryoneHaveTheSameSize() {
		
		boolean sameSize = true;
		
		for (int i=0; i<Network.size(); i++) {
			Node n = Network.get(i);
			
			if (!n.isUp()) {
				logger.info("node " + n.getID() + " failed");
				_numOfFailedNodes++;
				continue;
			}
			
			IFSAutomaton au = (IFSAutomaton) n.getProtocol(this._protocolID);
			Vector<String> dq = au.getCommandHistory();
			
			_numOfLiveNodes++;
			
			if (_FSMQueueSize==0) {
				_FSMQueueSize = dq.size();
			}
			
			if (_FSMQueueSize != dq.size()) {
				sameSize = false;
			}
		}
		return sameSize;	
	}

}
